package com.ms.orm.mapper;

import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author .
 */
public class SqlParser {

    /**
     * sql 映射文件
     */
    private static final String SQL_FILE_PATH = "classpath*:sql/**/*.sql";

    /**
     * ([^"]*) : 表示除双引号之外的任何序列
     * (.*?) .* 表示匹配任意长度的字符串，这是一种贪婪匹配，会尽可能多地匹配字符。而 .*? 则是在 * 后加上 ?，表示非贪婪或最小匹配，即尽可能少地匹配字符，只要满足条件就停止‌
     *
     */
    private static final Pattern IF_PATTERN = Pattern.compile("<if\\s+test=\"([^\"]*)\">(.*?)</if>", Pattern.DOTALL);


    /**
     *    // 定义正则表达式模式
     * 解析 sql文件，生成查询语句
     * @return
     * @throws Exception
     */
    public static Map<String, Map<String, SqlStatement>> parseSqlFiles() throws Exception {

        PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        Resource[] resources = resolver.getResources(SQL_FILE_PATH);
        //
        Map<String, Map<String, SqlStatement>> sqlStatementsByMapper = new HashMap<>();

        for (Resource resource : resources) {
            //获取 mapper 接口全限定名
            String mapperName = getMapperNameFromResource(resource);
            try (BufferedReader reader = new BufferedReader(new InputStreamReader(resource.getInputStream()))) {
                //解析mapper中方法
                Map<String, SqlStatement> sqlStatements = parseSqlFile(reader);
                sqlStatementsByMapper.put(mapperName, sqlStatements);
            }
        }
        return sqlStatementsByMapper;
    }

    private static String getMapperNameFromResource(Resource resource) {
        String path = resource.getFilename();
        if (path == null) {
            return null;
        }
        return path.replace(".sql", "").replace("/", ".");
    }



    /**
     * 解析文件
     * @param reader
     * @return Map<String, String>  key = 方法名，value = 对应的sql
     * @throws Exception
     */
    private static Map<String, SqlStatement> parseSqlFile(BufferedReader reader) throws Exception {
        Map<String, SqlStatement> sqlStatements = new HashMap<>();
        StringBuilder sqlBuilder = new StringBuilder();
        //sql语句
        String currentStatementName = null;
        String line;

        while ((line = reader.readLine()) != null) {
            line = line.trim();
            if (line.startsWith("-- name:")) {
                //1）遇到-- name:表示新的一个sql语句开始，2）currentStatementName不为nulll,表示上一句语句结束
                if (currentStatementName != null) {
                    sqlStatements.put(currentStatementName, processConditionalSql(sqlBuilder.toString().trim()));
                    // Clear the builder
                    sqlBuilder.setLength(0);
                }
                currentStatementName = line.substring(9).trim();
            } else {
                sqlBuilder.append(line).append("\n");
            }
        }
        //因为上述语句只能解析最后一个sql语句之前的语句
        //所以这里需要解析最后一个sql语句
        if (currentStatementName != null && sqlBuilder.length() > 0) {
            sqlStatements.put(currentStatementName, processConditionalSql(sqlBuilder.toString().trim()));
        }
        return sqlStatements;
    }

    /**
     * 1）name != null
     * 2）name != null && name != ""
     * 3）age != null || name != ""
     * 4）name??
     * 5）topicId != null and topicId != ''
     * 以及参数Map<String,Object> 解析得到具体表达式
     * 根据上述 表达时例子，解析得到
     * @param sql
     * @return
     */
    private static SqlStatement processConditionalSql(String sql) {
        Matcher matcher = IF_PATTERN.matcher(sql);
        StringBuffer baseSql = new StringBuffer();
        Map<String, String> conditions = new HashMap<>();

        int lastIndex = 0;
        while (matcher.find()) {
            //sql 添加遇到if 条件之前的 部分
            baseSql.append(sql, lastIndex, matcher.start());
            // 整个 <if> 标签内容
            String ifTagContent = matcher.group(0);
            //condition, example :  name != null
            String condition = matcher.group(1);
            //content, example : AND u.name LIKE :name
            String content = matcher.group(2).trim();
            //把条件 作为key,内容作为 值存入map
            conditions.put(condition, content);
            //sql中 加入模版字符串，在最中生成执行sql时候，根据实际条件替换
            baseSql.append(" ${" + condition + "} ");
            lastIndex = matcher.end();
        }
        //把条件之后的内容加入sql
        baseSql.append(sql.substring(lastIndex));
        System.err.println("baseSql.toString()=="+baseSql.toString());
        //创建 SqlStatement 并返回
        return new SqlStatement(baseSql.toString(), conditions);
    }

    static class SqlStatement {
        //基础sql 包含条件占位符，根据实际 条件替换
        private final String baseSql;
        //条件与条件成立后的内容
        private final Map<String, String> conditions;

        public SqlStatement(String baseSql, Map<String, String> conditions) {
            this.baseSql = baseSql;
            this.conditions = conditions;
        }

        public String getBaseSql() {
            return baseSql;
        }

        public Map<String, String> getConditions() {
            return conditions;
        }
    }
}
